package com.forsir.android.mhfinance.storeObjects;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import com.forsir.android.mhfinance.R;
import com.forsir.android.mhfinance.helper.DbAdapter;

public class Payment extends IStorable {
	public static final String TAG = "MHFinance";

	public static final int TYPE_WITHDRAW = 1;
	public static final int TYPE_DEPOSIT = 2;
	public static final int TYPE_REMAINDER = 3;
	public static final int TYPE_TRANSFER_FROM = 4;
	public static final int TYPE_TRANSFER_TO = 5;

	private static final String PAYMENT_ID = "payment_id";

	private float mAmount = 0;
	private float mRemainder = 0;
	private boolean mActualized = true; // if reminder type payment is actualized
	private String mDescription = "";
	private Account mAccount;
	private Account mTransferAccount = null;
	private Subcategory mSubcategory;
	private int mType = TYPE_WITHDRAW;
	private Calendar mDate = Calendar.getInstance();
	private Repeating mRepeating = new Repeating(Repeating.TYPE_NONE);

	// only for report
	public float summary_withdraw = 0;
	public float summary_deposit = 0;

	public Payment() {
		mDate.set(mDate.get(Calendar.YEAR), mDate.get(Calendar.MONTH), mDate.get(Calendar.DAY_OF_MONTH), mDate.get(Calendar.HOUR_OF_DAY), 0, 0);
		mDate.set(Calendar.MILLISECOND, 0);
	}

	public Payment(long id) {
		this();
		setId(id);
	}

	public Payment(Cursor cursor) {
		this();
		fillFromCursor(cursor);
	}

	public void setAmount(float value) {
		mAmount = value;
	}

	public float getAmount() {
		return mAmount;
	}

	public String getAmountString() {
		if (mType == TYPE_WITHDRAW || mType == TYPE_TRANSFER_FROM) {
			return String.format("%.2f", -mAmount);
		}
		return String.format("%.2f", mAmount);
	}

	public String getAmountStringWithdraw(String currencyBefore, float amount, String currencyAfter) {
		if (amount < 0) {
			return formatAmountString(currencyBefore, amount, currencyAfter);
		}
		return "";
	}

	public String getAmountStringDeposit(String currencyBefore, float amount, String currencyAfter) {
		if (amount < 0) {
			return "";
		}
		return formatAmountString(currencyBefore, amount, currencyAfter);
	}

	public static String formatAmountString(String currencyBefore, float amount, String currencyAfter) {
		return currencyBefore + String.format("%.2f", amount) + " " + currencyAfter;
	}

	public static String formatAmountString(String currencyBefore, String string, String currencyAfter) {
		final float amount = Float.parseFloat(string == null ? "" : string);
		if (Float.isNaN(amount)) {
			return formatAmountString(currencyBefore, 0, currencyAfter);
		}
		return formatAmountString(currencyBefore, amount, currencyAfter);
	}

	// Date functions
	public void setDate(Calendar date) {
		mDate = date;
	}

	public Calendar getDate() {
		return mDate;
	}

	public void setDateLong(long date) {
		mDate.setTimeInMillis(date);
	}

	public void setDate(int year, int month, int day) {
		mDate.set(Calendar.YEAR, year);
		mDate.set(Calendar.MONTH, month);
		mDate.set(Calendar.DAY_OF_MONTH, day);
	}

	public void setDateToNow() {
		final Calendar date = Calendar.getInstance();
		mDate.set(date.get(Calendar.YEAR), date.get(Calendar.MONTH), date.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
	}

	public long getDateLong() {
		return mDate.getTimeInMillis();
	}

	public String getDateString(Resources resource) {
		final SimpleDateFormat format = new SimpleDateFormat(resource.getString(R.string.format_date));
		return format.format(mDate.getTime());
	}

	public String getDateString() {
		final SimpleDateFormat format = new SimpleDateFormat("d.M.yyyy");
		return format.format(mDate.getTime());
	}

	public static String formatDateString(Long date, Resources resource) {
		final SimpleDateFormat format = new SimpleDateFormat(resource.getString(R.string.format_date));
		return format.format(new Date(date));
	}

	public int getYear() {
		return mDate.get(Calendar.YEAR);
	}

	public int getMonth() {
		return mDate.get(Calendar.MONTH);
	}

	public int getDay() {
		return mDate.get(Calendar.DAY_OF_MONTH);
	}

	// Time functions

	public boolean isTimeZero() {
		return mDate.get(Calendar.HOUR_OF_DAY) == 0 && mDate.get(Calendar.MINUTE) == 0;
	}

	public void setTime(int hour, int minute) {
		mDate.set(Calendar.HOUR_OF_DAY, hour);
		mDate.set(Calendar.MINUTE, minute);
	}

	public String getTimeString(Resources resource) {
		final SimpleDateFormat format = new SimpleDateFormat(resource.getString(R.string.format_time));
		return format.format(mDate.getTime());
	}

	public int getHour() {
		return mDate.get(Calendar.HOUR_OF_DAY);
	}

	public int getMinute() {
		return mDate.get(Calendar.MINUTE);
	}

	public void setDescription(String description) {
		mDescription = description;
	}

	public String getDescription() {
		return mDescription;
	}

	public float getRemainder() {
		return mRemainder;
	}

	public void setRemainder(float remainder) {
		mRemainder = remainder;
	}

	public boolean isActualized() {
		return mActualized;
	}

	public void setActualized(boolean actualized) {
		mActualized = actualized;
	}

	public Account getTransferAccount() {
		return mTransferAccount;
	}

	public void setTransferAccount(Account transferAccount) {
		mTransferAccount = transferAccount;
	}

	public void setTransferAccountId(long transferAccountId) {
		mTransferAccount = AccountsList.getAccountById(transferAccountId);
	}

	public void setAccount(Account account) {
		mAccount = account;
	}

	public void setAccountById(long accountId) {
		mAccount = AccountsList.getAccountById(accountId);
	}

	public Account getAccount() {
		return mAccount;
	}

	public void setSubcategory(Subcategory subcategory) {
		mSubcategory = subcategory;
	}

	public void setSubcategoryById(long subcategoryId) {
		mSubcategory = CategoryList.getSubcategoryById(subcategoryId);
	}

	public Subcategory getSubcategory() {
		return mSubcategory;
	}

	public void setType(int type) {
		mType = type;
	}

	public int getType() {
		return mType;
	}

	@Override
	public String getName() {
		return getAmountString();
	}

	public void setRepeating(Repeating mRepeating) {
		this.mRepeating = mRepeating;
	}

	public Repeating getRepeating() {
		return mRepeating;
	}

	private void setRepeatingId(long repeatingId) {
		mRepeating = RepeatingList.getRepeatingById(repeatingId);
	}

	public void deleteAll(DbAdapter dbHelper) {
		if (getId() > 0) {
			dbHelper.deleteEntry(this);
		}
		mRepeating.deleteAllPayments(dbHelper);
	}

	public void normalizeTransfer() {
		if (mType == TYPE_TRANSFER_TO) {
			mType = TYPE_TRANSFER_FROM;
			mAmount = -mAmount;
			final Account account = mAccount;
			mAccount = mTransferAccount;
			mTransferAccount = account;
		}
	}

	public CharSequence getTypeString(Resources resources) {
		CharSequence retString = "";
		switch (mType) {
			case TYPE_WITHDRAW:
				retString = resources.getText(R.string.type_withdraw);
				break;
			case TYPE_DEPOSIT:
				retString = resources.getText(R.string.type_deposit);
				break;
			case TYPE_REMAINDER:
				retString = resources.getText(R.string.type_remainder);
				break;
			case TYPE_TRANSFER_FROM:
				retString = resources.getText(R.string.type_transfer_from);
				break;
			case TYPE_TRANSFER_TO:
				retString = resources.getText(R.string.type_transfer_to);
				break;
		}
		return retString;
	}

	@Override
	public ContentValues getContent() {
		final ContentValues content = new ContentValues();
		content.put(DbAdapter.PAYMENT_ACCOUNT, mAccount.getId());
		content.put(DbAdapter.PAYMENT_TRANSFER_ACCOUNT, (mTransferAccount == null ? null : mTransferAccount.getId()));
		content.put(DbAdapter.PAYMENT_CATEGORY, (mSubcategory == null ? null : mSubcategory.getId()));
		content.put(DbAdapter.PAYMENT_DEPOSIT, (mAmount > 0 ? mAmount : 0));
		content.put(DbAdapter.PAYMENT_WITHDRAW, (mAmount > 0 ? 0 : mAmount));
		content.put(DbAdapter.PAYMENT_REMAINDER, mRemainder);
		content.put(DbAdapter.PAYMENT_ACTUALIZED, mActualized);
		content.put(DbAdapter.PAYMENT_TYPE, mType);
		content.put(DbAdapter.PAYMENT_DATE, getDateLong());
		content.put(DbAdapter.PAYMENT_MONTH, mDate.get(Calendar.MONTH));
		content.put(DbAdapter.PAYMENT_DESCRIPTION, mDescription);
		content.put(DbAdapter.PAYMENT_REPEATING, (mRepeating.getType() == Repeating.TYPE_NONE ? null : mRepeating.getId()));

		return content;
	}

	@Override
	public String getTableName() {
		return DbAdapter.PAYMENT_TABLE;

	}

	@Override
	public String[] getColumns() {
		return new String[] { DbAdapter.ID, DbAdapter.PAYMENT_ACCOUNT, DbAdapter.PAYMENT_TRANSFER_ACCOUNT, DbAdapter.PAYMENT_CATEGORY, DbAdapter.PAYMENT_DEPOSIT,
		        DbAdapter.PAYMENT_WITHDRAW, DbAdapter.PAYMENT_REMAINDER, DbAdapter.PAYMENT_ACTUALIZED, DbAdapter.PAYMENT_TYPE, DbAdapter.PAYMENT_DATE,
		        DbAdapter.PAYMENT_MONTH, DbAdapter.PAYMENT_DESCRIPTION, DbAdapter.PAYMENT_REPEATING };
	}

	@Override
	public IStorable fillFromCursor(Cursor cursor) {
		if (cursor == null) {
			setId(-1);
			return this;
		}

		if (!cursor.isAfterLast()) {
			// Columns ordered in getColumns
			setId(cursor.getLong(0));
			setAccountById(cursor.getLong(1));
			setTransferAccountId(cursor.getLong(2));
			setSubcategoryById(cursor.getLong(3));
			mAmount = cursor.getFloat(4) + cursor.getFloat(5);
			mRemainder = cursor.getFloat(6);
			mActualized = cursor.getString(7).contentEquals("true");
			mType = cursor.getInt(8);
			setDateLong(cursor.getLong(9));
			// month is not needed
			mDescription = cursor.getString(11);
			setRepeatingId(cursor.getLong(12));

			mDate.set(Calendar.SECOND, 0);
			mDate.set(Calendar.MILLISECOND, 0);
		}

		return this;
	}

	public Payment fillFromBundle(Bundle bundle) {
		if (bundle == null) {
			setId(-1);
			return this;
		}

		setId(bundle.getLong(PAYMENT_ID));
		setAccountById(bundle.getLong(DbAdapter.PAYMENT_ACCOUNT));
		setTransferAccountId(bundle.getLong(DbAdapter.PAYMENT_TRANSFER_ACCOUNT));
		setSubcategoryById(bundle.getLong(DbAdapter.PAYMENT_CATEGORY));
		mAmount = bundle.getFloat(DbAdapter.PAYMENT_DEPOSIT) + bundle.getFloat(DbAdapter.PAYMENT_WITHDRAW);
		mRemainder = bundle.getFloat(DbAdapter.PAYMENT_REMAINDER);
		mActualized = bundle.getBoolean(DbAdapter.PAYMENT_ACTUALIZED);
		mType = bundle.getInt(DbAdapter.PAYMENT_TYPE);
		setDateLong(bundle.getLong(DbAdapter.PAYMENT_DATE));
		mDescription = bundle.getString(DbAdapter.PAYMENT_DESCRIPTION);
		setRepeatingId(bundle.getLong(DbAdapter.PAYMENT_REPEATING));

		mDate.set(Calendar.SECOND, 0);
		mDate.set(Calendar.MILLISECOND, 0);
		return this;
	}

	public Bundle fillBundle(Bundle bundle) {
		if (bundle == null) {
			return null;
		}

		bundle.putLong(PAYMENT_ID, getId());
		bundle.putLong(DbAdapter.PAYMENT_ACCOUNT, mAccount.getId());
		bundle.putLong(DbAdapter.PAYMENT_TRANSFER_ACCOUNT, (mTransferAccount == null ? -1 : mTransferAccount.getId()));
		bundle.putLong(DbAdapter.PAYMENT_CATEGORY, (mSubcategory == null ? -1 : mSubcategory.getId()));
		bundle.putFloat(DbAdapter.PAYMENT_DEPOSIT, (mAmount > 0 ? mAmount : 0));
		bundle.putFloat(DbAdapter.PAYMENT_WITHDRAW, (mAmount > 0 ? 0 : mAmount));
		bundle.putFloat(DbAdapter.PAYMENT_REMAINDER, mRemainder);
		bundle.putBoolean(DbAdapter.PAYMENT_ACTUALIZED, mActualized);
		bundle.putInt(DbAdapter.PAYMENT_TYPE, mType);
		bundle.putLong(DbAdapter.PAYMENT_DATE, getDateLong());
		bundle.putString(DbAdapter.PAYMENT_DESCRIPTION, mDescription);
		bundle.putLong(DbAdapter.PAYMENT_REPEATING, (mRepeating == null ? -1 : mRepeating.getId()));

		return bundle;
	}

	/**
	 * change amount sing, or payment type by amount
	 */
	public void normalizeAmount(boolean changeType) {
		if (changeType) {
			if (mAmount < 0) {
				switch (mType) {
					case TYPE_WITHDRAW:
						mType = TYPE_DEPOSIT;
						break;
					case TYPE_DEPOSIT:
						mType = TYPE_WITHDRAW;
						break;
					case TYPE_TRANSFER_FROM:
						mType = TYPE_TRANSFER_TO;
						break;
					case TYPE_TRANSFER_TO:
						mType = TYPE_TRANSFER_FROM;
						break;
				}
			}
		}
		// TYPE_WITHDRAW and TYPE_TRANSFER_FROM must be negative
		mAmount = Math.abs(mAmount);
		if (mType == Payment.TYPE_WITHDRAW || mType == Payment.TYPE_TRANSFER_FROM) {
			mAmount = -mAmount;
		}
	}

	public void normalizeDate() {
		mDate.set(Calendar.SECOND, 0);
		mDate.set(Calendar.MILLISECOND, 0);
	}

	public boolean checkValues(Context context) {
		if (mAccount == null) {
			Toast.makeText(context, R.string.warning_account, Toast.LENGTH_LONG).show();
			return false;
		}
		if (mType == Payment.TYPE_TRANSFER_TO || mType == Payment.TYPE_TRANSFER_FROM) {
			if (mTransferAccount == null) {
				Toast.makeText(context, R.string.warning_account_transfer, Toast.LENGTH_LONG).show();
				return false;
			}
			mSubcategory = null;
		} else {
			if (mSubcategory == null) {
				Toast.makeText(context, R.string.warning_category, Toast.LENGTH_LONG).show();
				return false;
			}
		}
		return true;
	}

	public void copyPayment(Payment sourcePayment) {
		mAccount = sourcePayment.getAccount();
		mTransferAccount = sourcePayment.getTransferAccount();
		mSubcategory = sourcePayment.getSubcategory();
		mAmount = sourcePayment.getAmount();
		mRemainder = sourcePayment.getRemainder();
		mActualized = sourcePayment.isActualized();
		mType = sourcePayment.getType();
		setDateLong(sourcePayment.getDateLong());
		mDescription = sourcePayment.getDescription();
		mRepeating = sourcePayment.getRepeating();

		mDate.set(Calendar.SECOND, 0);
		mDate.set(Calendar.MILLISECOND, 0);
	}

	/**
	 * updates next remainder
	 */
	public void updateNextRemainder(DbAdapter dbHelper) {
		final Payment transferPayment = new Payment();
		String where;
		Cursor cursor;

		if (mType != TYPE_REMAINDER) {
			// compute remainder payment in same time
			where = DbAdapter.PAYMENT_TYPE + "=" + Payment.TYPE_REMAINDER + " and " + DbAdapter.PAYMENT_DATE + "=" + getDateLong() + " and " + DbAdapter.PAYMENT_ACCOUNT
			        + "=" + mAccount.getId();
			cursor = dbHelper.fetchEntry(DbAdapter.PAYMENT_TABLE, transferPayment.getColumns(), where, null, "", "", DbAdapter.ID);
			if (cursor.moveToFirst()) {
				transferPayment.fillFromCursor(cursor);
				Log.d(TAG, "updateNextRemainder - transferPayment1:" + transferPayment.getDateString());
				transferPayment.computeRemainder(null, dbHelper);
				dbHelper.updateEntry(transferPayment);

				cursor.close();
				return;
			} else {
				cursor.close();
			}
		}

		// get last reminder
		where = DbAdapter.PAYMENT_TYPE + "=" + Payment.TYPE_REMAINDER + " and " + DbAdapter.PAYMENT_DATE + ">" + getDateLong() + " and " + DbAdapter.PAYMENT_ACCOUNT
		        + "=" + mAccount.getId();
		cursor = dbHelper.fetchEntry(DbAdapter.PAYMENT_TABLE, transferPayment.getColumns(), where, null, "", "", DbAdapter.PAYMENT_DATE, 1);
		if (cursor.moveToFirst()) {
			transferPayment.fillFromCursor(cursor);
		} else {
			cursor.close();
			return;
		}
		cursor.close();

		Log.d(TAG, "updateNextRemainder - transferPayment2:" + transferPayment.getDateString());
		transferPayment.computeRemainder(null, dbHelper);
		dbHelper.updateEntry(transferPayment);
	}

	/**
	 * compute remainder value
	 */
	public void computeRemainder(Payment firstRemainder, DbAdapter dbHelper) {
		if (mType != TYPE_REMAINDER) {
			return;
		}

		float firstAmount = 0;
		String where = "";
		Cursor cursor;
		// get reminder before and fills condition
		if (firstRemainder == null) {
			Log.d(TAG, "computeRemainder - get first remainder");
			where = DbAdapter.PAYMENT_TYPE + "=" + Payment.TYPE_REMAINDER + " and " + DbAdapter.PAYMENT_DATE + "<" + getDateLong() + " and " + DbAdapter.PAYMENT_ACCOUNT
			        + " = " + mAccount.getId();
			cursor = dbHelper.fetchEntry(DbAdapter.PAYMENT_TABLE, new String[] { DbAdapter.PAYMENT_DATE, DbAdapter.PAYMENT_REMAINDER }, where, null, "", "",
			        DbAdapter.PAYMENT_DATE + " desc", 1);
			if (cursor.moveToFirst()) {
				where = cursor.getLong(0) + "<" + DbAdapter.PAYMENT_DATE + " and ";
				firstAmount = cursor.getFloat(1);
				Log.d(TAG, "computeRemainder - firstAmount:" + firstAmount);
			} else {
				where = "";
			}
			cursor.close();
		} else {
			where = firstRemainder.getDateLong() + "<" + DbAdapter.PAYMENT_DATE + " and ";
			firstAmount = firstRemainder.getRemainder();
			Log.d(TAG, "computeRemainder - firstAmount:" + firstAmount);
		}

		// count values
		where += DbAdapter.PAYMENT_DATE + "<=" + getDateLong() + " and " + DbAdapter.PAYMENT_TYPE + "<>" + Payment.TYPE_REMAINDER;

		String[] columns = new String[] { "total(" + DbAdapter.PAYMENT_DEPOSIT + ") as deposit", "total(" + DbAdapter.PAYMENT_WITHDRAW + ") as withdraw" };
		cursor = dbHelper
		        .fetchEntry(DbAdapter.PAYMENT_TABLE, columns, where + " and " + DbAdapter.PAYMENT_ACCOUNT + " = " + getAccount().getId(), null, null, null, null);
		float deposit = cursor.getFloat(0);
		float withdraw = cursor.getFloat(1);
		cursor.close();

		// withdraw and deposit must be switched
		columns = new String[] { "total(-" + DbAdapter.PAYMENT_WITHDRAW + ") as deposit", "total(-" + DbAdapter.PAYMENT_DEPOSIT + ") as withdraw", };
		cursor = dbHelper.fetchEntry(DbAdapter.PAYMENT_TABLE, columns, where + " and " + DbAdapter.PAYMENT_TRANSFER_ACCOUNT + " = " + getAccount().getId(), null, null,
		        null, null);
		deposit += cursor.getFloat(0);
		withdraw += cursor.getFloat(1);
		cursor.close();

		// withdraw is negative, I add absolute value of withdraw
		mAmount = mRemainder - firstAmount - deposit - withdraw;
		Log.d(TAG, "computeRemainder - mAmount:" + mAmount);
	}
}
